package net.stock.service;

import net.stock.db.DayPrice;
import net.stock.db.StockItem;
import net.stock.domain.ItemPrice;
import net.stock.utils.StockUtils;
import org.joda.time.DateTime;

import javax.jdo.Extent;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author naim
 * @date Dec 23, 2009
 */

public class StockJdoDao extends JdoDaoSupport implements StockDao {
    private Logger log = Logger.getLogger(getClass().getName());

    public Map<String, List<ItemPrice>> getNotifMapForAllUsers(Map<String, Float> latestPriceMap) {
        Map<String, Float> lastSavedPriceMap = getPriceMap(new DateTime(StockUtils.timeZone));

        Map<String, List<ItemPrice>> notifMapForAllUsers = new HashMap<String, List<ItemPrice>>();

        PersistenceManager pm = getPersistenceManager();
        try {
            Extent<StockItem> extent = pm.getExtent(StockItem.class, false);
            for(StockItem si : extent) {
                boolean isLow = false;
                boolean isHigh = false;
                Float latestPrice = latestPriceMap.get(si.getName());
                Float lastSavedPrice = lastSavedPriceMap.get(si.getName());

                if (!StockUtils.priceHasChangedSignificantly(lastSavedPrice, latestPrice)) {
                    continue;
                } 

                if (si.getLow() != null && latestPrice <= si.getLow()) {
                    isLow = true;
                }

                if (si.getHigh() != null && latestPrice >= si.getHigh()) {
                    isHigh = true;
                }

                List<ItemPrice> notifListPerUser = notifMapForAllUsers.get(si.getEmail());
                if (notifListPerUser == null) {
                    notifListPerUser = new ArrayList<ItemPrice>();
                    notifMapForAllUsers.put(si.getEmail(), notifListPerUser);
                }
                notifListPerUser.add(new ItemPrice(si.getName(), latestPrice, si.getMyPrice(), isLow, isHigh));
            }
            extent.closeAll();
            return notifMapForAllUsers;
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public Collection<StockItem> getStockItems(String email) {
        PersistenceManager pm = getPersistenceManager();
        Query q = pm.newQuery(StockItem.class, "email == '" + email + "'");
        try {
            return pm.detachCopyAll((Collection<StockItem>) q.execute());
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public void saveStockItem(StockItem si) {
        PersistenceManager pm = getPersistenceManager();
        try {
            pm.makePersistent(si);
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public void deleteStockItem(String owner, Long id) {
        PersistenceManager pm = getPersistenceManager();
        try {
            StockItem si = pm.getObjectById(StockItem.class, id);
            if (owner.equals(si.getEmail())) {
                pm.deletePersistent(si);
            } else {
                throw new RuntimeException("User is trying to delete an object owned by someone else.");
            }
        } catch (JDOObjectNotFoundException notFoundException) {
            log.log(Level.INFO, "No StockItem (id: " + id + ") found in DB to delete");
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public void saveDayPrices(DateTime date, Map<String, Float> priceMap) {
        PersistenceManager pm = getPersistenceManager();
        int dateInt = StockUtils.toDateInt(date);

        log.log(Level.INFO, "Saving Price for Today, dateInt: " + dateInt);

        Collection<DayPrice> dayPrices = new ArrayList<DayPrice>(priceMap.size());
        for (String item : priceMap.keySet()) {
            dayPrices.add(new DayPrice(dateInt, item, priceMap.get(item)));
        }

        try {
            pm.makePersistentAll(dayPrices);
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public void deletePrices(DateTime date) {
        PersistenceManager pm = getPersistenceManager();
        Query q = pm.newQuery(DayPrice.class, "dateInt == date_int");
        q.declareParameters("int date_int");
        int dateInt = StockUtils.toDateInt(date);
        try {
            Collection<DayPrice> prices = (Collection<DayPrice>) q.execute(dateInt);
            if (prices.size() > 0) {
                pm.deletePersistentAll(prices);
            }
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public Collection<DayPrice> getDayPrices(String item) {
        PersistenceManager pm = getPersistenceManager();
        Query q = pm.newQuery(DayPrice.class, "item == '" + item + "'");
        q.setOrdering("dateInt descending");
        try {
            return pm.detachCopyAll((Collection<DayPrice>) q.execute());
        } finally {
            releasePersistenceManager(pm);
        }
    }

    public Map<String, Float> getPriceMap(DateTime date) {
        Map<String, Float> todaysPriceMap = new HashMap<String, Float>();
        PersistenceManager pm = getPersistenceManager();
        Query q = pm.newQuery(DayPrice.class, "dateInt == date_int");
        q.declareParameters("int date_int");
        q.setOrdering("item ascending");
        int dateInt = StockUtils.toDateInt(date);
        try {
            Collection<DayPrice> todaysPrices =  (Collection<DayPrice>) q.execute(dateInt);
            for (DayPrice dp: todaysPrices) {
                todaysPriceMap.put(dp.getItem(), dp.getPrice());
            }
        } finally {
            releasePersistenceManager(pm);
        }
        return todaysPriceMap;
    }
}
